package com.yiba.vpn.util;

import com.alibaba.fastjson.JSONObject;

import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.util.HashMap;

public class UDPUtils {

    private static final String UTF8 = "UTF-8";
    private static final String OK = "ok";
    private static final String PING = "ping";
    private static final String ADD = "add: ";
    private static final String REMOVE = "remove: ";

    //java UDPUtils "127.0.0.1" "4000" "add: {\"server_port\": 8001, \"password\":\"7cd308cc059\"}"
    //#ok
    //java UDPUtils "127.0.0.1" "4000" "remove: {\"server_port\": 8001}"
    //#ok
    //java UDPUtils "127.0.0.1" "4000" "ping"
    //#stat: {"8001":460286,"8002":0}
    public static void main(String[] args) throws Exception {
        NodeInfo nodeInfo = new NodeInfo();
        nodeInfo.setHost("47.92.165.1");
        int port = 48827;
       // long flow = getFlow(nodeInfo, port);
        NodeInfo nodeInfos = new NodeInfo("47.92.165.1", 4000);
        HashMap<String, Long> result = UDPUtils.getFlows(nodeInfos);
        Long flows = result.get(48827) ==null ? 0L : result.get(48827);

        System.out.println("flow=" + flows);
//        if (args == null) {
//            System.out.println("args is null.");
//            return;
//        } else {
//            if (args.length != 3) {
//                NodeInfo nodeInfo = new NodeInfo("127.0.0.1", 4000);
//                AddInfo addInfo = new AddInfo(10087, "marking");
//                RemoveInfo removeInfo = new RemoveInfo(10087);
////                boolean result = add(nodeInfo, addInfo);
////                boolean result = remove(nodeInfo, removeInfo);
//                HashMap<String, Long> result = getFlows(nodeInfo);
//                System.out.println("result=" + result);
//                for (String arg : args) {
//                    System.out.println("arg=" + arg);
//                }
//                return;
//            }
//        }
//        String host = args[0];
//        int port = Integer.valueOf(args[1]);
//        String dataStr = args[2];
//        System.out.println("host=" + host + ";port=" + port + ";dataStr=" + dataStr);
//        byte[] data = dataStr.getBytes();
//        byte[] result = send(host, port, data);
//        System.out.println("result=" + new String(result, UTF8));
    }

    public static boolean add(NodeInfo nodeInfo, AddInfo addInfo) {
        return commonCheck(nodeInfo, addInfo.getData());
    }

    public static boolean remove(NodeInfo nodeInfo, RemoveInfo removeInfo) {
        return commonCheck(nodeInfo, removeInfo.getData());
    }

    private static final String FLOW_MARK = "ok_";
    private static final int GET_FLOW_PORT = 22222;

    public static long getFlow(NodeInfo nodeInfo, int port) {
        nodeInfo.setPort(GET_FLOW_PORT);
        try {
            byte[] data = commonSend(nodeInfo, String.valueOf(port).getBytes());
            if (data != null) {
                String dataStr = new String(data);
                if (dataStr.startsWith(FLOW_MARK)) {
                    return Long.valueOf(dataStr.replace(FLOW_MARK, ""));
                }
            }
        } catch (Exception e) {
            return -2;
        }
        return -1;
    }

    public static HashMap<String, Long> getFlows(NodeInfo nodeInfo) {
        byte[] tempData;
        JSONObject jsonObject;
        HashMap<String, Long> result = new HashMap<>();
        try {
            tempData = commonSend(nodeInfo, PING.getBytes());
            jsonObject = JSONObject.parseObject(new String(tempData));
        } catch (Exception e) {
            System.out.println("e=" + e);
            return null;
        }
        Long value = null;
        for (String key : jsonObject.keySet()) {
            try {
                value = jsonObject.getLongValue(key);
            } catch (Exception e) {
                System.out.println("e=" + e);
            }
            result.put(key, value);
        }
        return result;
    }

    private static boolean commonCheck(NodeInfo nodeInfo, byte[] data) {
        byte[] tempData;
        try {
            tempData = commonSend(nodeInfo, data);
        } catch (Exception e) {
            System.out.println("e=" + e);
            return false;
        }
        return tempData != null && OK.equals(new String(tempData));
    }

    private static byte[] commonSend(NodeInfo nodeInfo, byte[] data) throws Exception {
        return send(nodeInfo.getHost(), nodeInfo.getPort(), data);
    }

    public static byte[] send(String host, int outPort, byte[] data) throws Exception {
        DatagramSocket datagramSocket = new DatagramSocket();
        datagramSocket.setSoTimeout(5000);
        InetAddress inetAddress = InetAddress.getByName(host);
        DatagramPacket request = new DatagramPacket(data, 0, data.length, inetAddress, outPort);
        datagramSocket.send(request);
        byte[] tempResult = new byte[1024000];
        DatagramPacket response = new DatagramPacket(tempResult, 0, tempResult.length);
        datagramSocket.receive(response);
        byte[] result = new byte[response.getLength()];
        System.arraycopy(tempResult, response.getOffset(), result, response.getOffset(), response.getLength());
        return result;
    }

    public static class NodeInfo {
        private String host;
        private Integer port;

        public NodeInfo() {
        }

        public NodeInfo(String host, Integer port) {
            this.host = host;
            this.port = port;
        }

        public String getHost() {
            return host;
        }

        public void setHost(String host) {
            this.host = host;
        }

        public int getPort() {
            return port;
        }

        public void setPort(int port) {
            this.port = port;
        }

        @Override
        public String toString() {
            return "NodeInfo{" +
                    "host='" + host + '\'' +
                    ", port=" + port +
                    '}';
        }
    }

    public static class AddInfo {
        private int serverPort;
        private String password;

        public AddInfo() {
        }

        public AddInfo(int serverPort, String password) {
            this.serverPort = serverPort;
            this.password = password;
        }

        public int getServerPort() {
            return serverPort;
        }

        public void setServerPort(int serverPort) {
            this.serverPort = serverPort;
        }

        public String getPassword() {
            return password;
        }

        public void setPassword(String password) {
            this.password = password;
        }

        @Override
        public String toString() {
            return "AddInfo{" +
                    "serverPort=" + serverPort +
                    ", password='" + password + '\'' +
                    '}';
        }

        public byte[] getData() {
            return (ADD + "{\"server_port\":" + serverPort + ",\"password\":\"" + password + '\"' + '}').getBytes();
        }
    }

    public static class RemoveInfo {
        private int serverPort;

        public RemoveInfo() {
        }

        public RemoveInfo(int serverPort) {
            this.serverPort = serverPort;
        }

        public int getServerPort() {
            return serverPort;
        }

        public void setServerPort(int serverPort) {
            this.serverPort = serverPort;
        }

        @Override
        public String toString() {
            return "RemoveInfo{" +
                    "serverPort=" + serverPort +
                    '}';
        }

        public byte[] getData() {
            return (REMOVE + "{\"server_port\":" + serverPort + '}').getBytes();
        }
    }

}
